/* 
** This JavaScript file provides any labyrinth viewer with the essential 
** functions required to interact with the system. Loading screens and 
** errors are displayed, state data is updated and nodes are fetched.
** 
** There are a few important callback functions that all viewers need 
** to implement:
** 
** -initialize()
** -nodeUpdated(nodeData)
** -stateUpdated(stateData)
*/ 

var checkLabyrinthState = false;
var nodeMask, currentNodeID;
var nodeData = {};
var stateData = {};
var node;

/* 
** This function sets up the session, requests the root node, and initiates
** a task that keeps the viewer state in sync with the server-side session.
*/ 
$(document).ready(function() {
    
    // If a session ID has been provided, update the cookie using this ID
    // so we can pickup on the pre-existing session BEFORE doing any requests.
    if(sessionID != 'None')
        $.cookie('sessionid', sessionID);
    
    // Call the viewer skin's initialize method to setup the UI layout.
    initialize();
    
    // Define a loading mask that will be used to lock the UI whenever a new node is requested.
    nodeMask = $('<div></div>')
		.html('Loading node data...')
		.dialog({
			title: 'Loading...',
			modal: true,
			resizable: false,
			closeOnEscape: false,
            open: function(event, ui) { $(".ui-dialog-titlebar-close").hide(); }
		}
    );
    
    /*
    ** Begin a task that updates the labyrinth state data every two seconds.
    */
    checkLabyrinthState = $.PeriodicalUpdater(siteRoot+'session/read/', {
        method: 'get',          // method; get or post
        data: '', 		          // array of values to be passed to the page - e.g. {name: "John", greeting: "hello"}
        minTimeout: 1000,       // starting value for the timeout in milliseconds
        maxTimeout: 8000,       // maximum length of time between requests
        multiplier: 2,          // if set to 2, timerInterval will double each time the response hasn't changed (up to maxTimeout)
        type: 'text',           // response type - text, xml, json, etc.  See $.ajax config options
        maxCalls: 0,            // maximum number of calls. 0 = no limit.
        autoStop: 0             // automatically stop requests after this many returns of the same data. 0 = disabled.
        }, handleStateData
    );
    
    // Load the root node of the labyrinth; a nodeID of false is a sentinel
    // value used to request the root node of a labyrinth.
    loadNode(false);
});

/*
** This function sends an Ajax request and parses the returned XML, which
** contains all of the information relevant to this node. Helper functions
** are used to redraw the links and counters.
*/
function loadNode(nodeID) {

    // Save the current link ID so that we can reload the node if a pause occurs    
    currentNodeID = nodeID;
    
    if(nodeID) {
        // Build the request URL using the appropriate variables
        var urlPath = siteRoot+'labyrinth/'+labyrinthID+'/node/'+nodeID+'/data/';
    } else {
        // We're requesting the root node of the labyrinth.
        var urlPath = siteRoot+'labyrinth/'+labyrinthID+'/root/data/';
    }

    // Stop the state check and clock updates
    checkLabyrinthState.stop();
    
    // Lock the UI until the request completes
    nodeMask.dialog('open');
    
    // Instantiate an Ajax request
    $.ajax({
        url: urlPath,
        success: handleNodeData,
        dataType: 'xml',
        error: function(response) {
            
            // Unlock the UI
            nodeMask.dialog('close');
            
            // Restart the state checker
            checkLabyrinthState.start();
        }
    });
}

/* 
** Event handler for successful ajax-based node requests.
** Parses the returned XML data into several key variables and calls the
** viewer skin's nodeUpdated method, handing it the new data in the process.
*/
function handleNodeData(response) {
    /* 
    ** Check for errors and display a modal dialog if required.
    */ 
    var error = $(response).find('error');
    if(error.length) {
        
        // Unlock the UI
        nodeMask.dialog('close');
        
        // Display a dialog to choose between the report and returning home.
        $('<div></div>')
    		.html($(error).find('message').text())
    		.dialog({
			resizable: false,
			height:140,
			modal: true,
			buttons: {
				'Retry': function() {
					$(this).dialog('close');
                    loadNode(currentNodeID);
				},
				'Exit Labyrinth': function() {
					$(this).dialog('close');
					document.location = siteRoot;
				}
			}
		});
        
        return;
    }
    
    /* 
    ** Try to parse the new XML; if it fails, restore the previous data.
    */ 
    var oldNodeData = nodeData;
    var alerts, counters, links;
    try {
        nodeData.title = $(response).find('node title').text();
        nodeData.message = $(response).find('node message').text();
        nodeData.supporting_information = $(response).find('node supporting_information').text();
        nodeData.avatar = $(response).find('node avatar').text();
        nodeData.alerts = $(response).find('node alerts alert').text();
        
        // Parse the counters and their child data into an array of dictionaries.
        counters = $(response).find('node counters counter');
        nodeData.counters = new Array();
        $(counters).each(function(index, counter) {
            var tempCounter = {};
            tempCounter.id = $(counter).attr('id');
            tempCounter.name = $(counter).find('name').text();
            tempCounter.description = $(counter).find('description').text();
            tempCounter.value = $(counter).find('value').text();
            tempCounter.visible = $(counter).find('visible').text();
            nodeData.counters[nodeData.counters.length] = tempCounter;
        });
        
        // Parse the link and their child data into an array of dictionaries.
        links = $(response).find('node links link');
        nodeData.links = new Array();
        $(links).each(function(index, link) {
            var tempLink = {};
            tempLink.id = $(link).attr('id');
            tempLink.icon = $(link).find('icon').text();
            tempLink.label = $(link).find('label').text();
            tempLink.destination = $(link).find('destination').text();
            nodeData.links[nodeData.links.length] = tempLink;
        });
        
        if(nodeData.avatar.length) {
            nodeData.avatar.title = nodeData.avatar[0].attributes.getNamedItem('title').value
            
            // Get the parameters required to properly display the avatar.
            var avatarParameters = {};
            var parameters = $(response).find('node avatar parameter');
            $(parameters).each(function(index, parameter){
                avatarParameters[parameter.attributes.getNamedItem('id').value] = parameter.childNodes[0].nodeValue;
            });
            nodeData.avatar.parameters = avatarParameters;
        }
    }
    catch(error) {
        nodeData = oldNodeData;
        console.log(error);
        return;
    }
    
    /* 
    ** Call the viewer skin to update the UI with the new node data.
    */ 
    nodeUpdated(nodeData);
}


/* 
** Event handler for successful ajax-based state requests.
** Parses the returned XML data into several key variables and calls the
** viewer skin's stateUpdated method, handing it the new data in the process.
*/
function handleStateData(response){
    /* 
    ** Try to parse the new XML; if it fails, restore the previous data.
    */ 
    var oldStateData = stateData;
    try {
        stateData.paused = $(response).find('pause').attr('state');
        stateData.message = $(response).find('pause').attr('message');
        stateData.timeRemaining = $(response).find('time').attr('remaining');
    }
    catch(error) {
        stateData = oldStateData;
        console.log(error);
        return;
    }
    
    /* 
    ** Call the viewer skin to update the UI with the new state data.
    */ 
    stateUpdated(stateData);
}